<!doctype html>
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<html>
<head>

  <meta charset="utf-8">
  <script>
  WCT = {waitFor: function (cb) {HTMLImports.whenReady(cb)}}
  </script>
  <script src="./test-flags.js"></script>
  <script src="../node_modules/wct-browser-legacy/browser.js"></script>
  <script src="../node_modules/@webcomponents/webcomponents-platform/webcomponents-platform.js"></script>
  <script src="../node_modules/es6-promise/dist/es6-promise.auto.min.js"></script>
  <script src="../node_modules/@webcomponents/template/template.js"></script>
  <script src="../node_modules/@webcomponents/html-imports/html-imports.min.js"></script>
  <script src="../node_modules/@webcomponents/shadydom/shadydom.min.js"></script>
  <script src="../node_modules/@webcomponents/custom-elements/custom-elements.min.js"></script>
  <script src="../scoping-shim.min.js"></script>
  <script src="../apply-shim.min.js"></script>
  <script>
  if (customElements.polyfillWrapFlushCallback) {
    // delay definition of custom-style until after template polyfill loads
    customElements.polyfillWrapFlushCallback(function(cb) {
      HTMLImports.whenReady(cb);
    });
  }
  </script>
  <script src="../custom-style-interface.min.js"></script>
  <script src="module/generated/custom-style-element.js"></script>
  <script src="module/generated/make-element.js"></script>
  <title>Apply Shim</title>

</head>
<body>
  <template id="basic">
    <style>
    :host {
      --mixin: {
        border: 2px solid black;
      };
    }
    div {
      @apply --mixin;
    }
    </style>
  </template>

  <template id="defaults">
    <style>
    :host {
      --mixin: {
        border: 2px solid black;
      }
    }
    div {
      border: 1px dotted orange;
      @apply --mixin;
    }
    span {
      border: inherit;
      @apply --mixin;
    }
    span {
      border: initial;
      @apply --mixin;
    }
    </style>
  </template>

  <template id="override">
    <style>
    :host {
      --override: {
        padding: 2px;
      };
    }
    :host([override]) {
      --override: {
        border: 2px solid black;
      };
    }
    div {
      @apply --override;
    }
    </style>
  </template>

  <template id="override-with-property">
    <style>
    :root {
      --prop-mixin: {
        border: 2px solid black;
      };
    }
    x-foo {
      --prop-mixin: blue;
      color: var(--prop-mixin);
    }
    div {
      @apply --prop-mixin;
    }
    </style>
  </template>

  <template id="define-with-var">
    <style>
    :root {
      --mixin-var: {
        border: 2px solid black;
      };
    }
    div {
      --mixin-var2: var(--mixin-var);
    }
    span {
      --mixin-var: 20px;
      --variable: var(--mixin-var);
    }
    </style>
  </template>

  <template id="x-element">
    <style>
    :host {
      @apply --my-mixin;
    }
    </style>
  </template>

  <template id="x-element2">
    <custom-style>
    <style>
    html {
      --my-mixin: {
        border: 2px solid black;
      };
    }
    </style>
    </custom-style>
  </template>
  <template id="important">
    <style>
    :host {
      --mixin-important: {
        background-color: white;
        border: 2px solid black !important;
        color: white !important;
      };
      --mixin: {
        background-color: red;
        border: 1px dotted orange;
        color: black !important;
      };
    }
    div {
      @apply --mixin-important;
      @apply --mixin;
    }
    </style>
  </template>
  <script>
  suite('Apply Shim', function() {
    function copy(name) {
      var template = document.querySelector('template#' + name);
      return template.content.cloneNode(true);
    }

    function prep(templateName, elementName) {
      var style = copy(templateName).querySelector('style');
      var ast = window.ShadyCSS.ApplyShim.transformStyle(style, elementName);
      return {style: style, ast: ast};
    }

    suite('Basic', function() {
      var style, ast;
      suiteSetup(function() {
        var info = prep('basic');
        style = info.style;
        ast = info.ast;
        style.textContent = window.ShadyCSS.ScopingShim.styleAstToString(ast);
      });

      test('style is transformed', function() {
        var orig = copy('basic').querySelector('style');
        assert.notEqual(style.textContent, orig.textContent);
      });

      test('mixin became custom properties', function() {
        var definition = ast.rules[0];
        var application = ast.rules[1];
        assert.match(definition.cssText, /--mixin_-_border:\s*2px solid black/);
        assert.match(application.cssText, /border:\s*var\(--mixin_-_border\)/);
      });
    });
    suite('Defaults', function() {
      var style, ast; // eslint-disable-line no-unused-vars
      suiteSetup(function() {
        var info = prep('defaults');
        style = info.style;
        ast = info.ast;
      });

      test('properties defined before mixin are used as defaults', function() {
        var application = ast.rules[1];
        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*1px dotted orange\)/);
      });

      test('inherit and initial default values are preserved', function() {
        var application = ast.rules[2];
        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*inherit\)/);
        application = ast.rules[3];
        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*initial\)/);
      });
    });

    suite('override', function() {
      var style, ast; // eslint-disable-line no-unused-vars
      suiteSetup(function() {
        var info = prep('override');
        style = info.style;
        ast = info.ast;
      });

      test('mixin redefinition sets unused properties to initial', function() {
        var def1 = ast.rules[0];
        assert.match(def1.cssText, /--override_-_padding:\s*2px/);
        var def2 = ast.rules[1];
        assert.match(def2.cssText, /--override_-_padding:\s*initial/);
        assert.match(def2.cssText, /--override_-_border:\s*2px solid black/);
      });

      test('mixin application includes all values', function() {
        var application = ast.rules[2];
        assert.match(application.cssText, /padding:\s*var\(--override_-_padding\)/);
        assert.match(application.cssText, /border:\s*var\(--override_-_border\)/);
      });
    });

    suite('override with property', function() {
      var style, ast; // eslint-disable-line no-unused-vars
      suiteSetup(function() {
        var info = prep('override-with-property');
        style = info.style;
        ast = info.ast;
      });

      test('mixin definition defers to property definition', function() {
        var def = ast.rules[1];
        assert.notMatch(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
      });

      test('mixin can still be used by other parts of the page', function() {
        var def = ast.rules[2];
        assert.match(def.cssText, /border:\s*var\(--prop-mixin_-_border\)/);
      });
    });

    suite('define with var()', function() {
      var style, ast; // eslint-disable-line no-unused-vars
      suiteSetup(function() {
        var info = prep('define-with-var');
        style = info.style;
        ast = info.ast;
      });

      test('mixin-var2 is defined with mixin-var\'s values', function() {
        var def = ast.rules[1];
        assert.match(def.cssText, /--mixin-var2_-_border:\s*var\(--mixin-var_-_border\)/);
      });

      test('var usage of mixin is not removed, preserving override functionality', function() {
        var def = ast.rules[2];
        assert.match(def.cssText, /--variable:\s*var\(--mixin-var\)/);
      });
    });

    suite('invalidation on new definitions', function() {
      var style, ast, element;
      suiteSetup(function() {
        makeElement('x-element');
        element = document.createElement('x-element');
        document.body.appendChild(element);
        style = element.shadowRoot ? element.shadowRoot.querySelector('style') : document.head.querySelector('style[scope=x-element]');
      });

      test('element initially has no definition', function() {
        var ast = window.ShadyCSS.ScopingShim._styleInfoForNode(element)._getStyleRules();
        assert.equal(ast.rules[0].cssText, ';');
      });

      test('Revalidating Apply Shim on element template fills in properties', function() {
        var nodes = copy('x-element2');
        document.body.appendChild(nodes);
        window.ShadyCSS.styleDocument();
        var ast = window.ShadyCSS.ScopingShim._styleInfoForNode(element)._getStyleRules();
        if (window.ShadyCSS.nativeCss) {
          assert.match(ast.rules[0].cssText, /border:\s*var\(--my-mixin_-_border\)/);
        } else {
          assert.match(ast.rules[0].cssText, /border:\s*2px solid black/);
        }
      });
    });
    suite('!important', function() {
      var ast;
      suiteSetup(function() {
        var info = prep('important');
        ast = info.ast;
      });

      test('!important in mixin correctly translates to !important in resulting custom property', function() {
        var application = ast.rules[1];
        assert.match(application.cssText, /border:\s*var\(--mixin-important_-_border\)\s*!important/);
      });
      test("Fallback of related property without !important is kept without !important in resulting custom property", function() {
        var application = ast.rules[1];
        assert.match(application.cssText, /border:\s*var\(--mixin_-_border,\s*var\(--mixin-important_-_border\)\)/);
      });
      test('Two mixins with both !important are treated in correct order while also preserving !important in resulting custom property', function () {
        var application = ast.rules[1];
        assert.match(application.cssText, /color:\s*var\(--mixin-important_-_color\)\s*!important/);
        assert.match(application.cssText, /color:\s*var\(--mixin_-_color,\s*var\(--mixin-important_-_color\)\)\s*!important/);
      })
      test('Properties without !important in a mixin with !important are treated independently', function() {
        var application = ast.rules[1];
        assert.match(application.cssText, /background-color:\s*var\(--mixin_-_background-color,\s*var\(--mixin-important_-_background-color\)\)/);
        assert.notMatch(application.cssText, /background-color:\s*var\(--mixin_-_background-color,\s*var\(--mixin-important_-_background-color\)\)\s*!important/);

        assert.match(application.cssText, /background-color:\s*var\(--mixin-important_-_background-color\)/);
        assert.notMatch(application.cssText, /background-color:\s*var\(--mixin-important_-_background-color\)\s*!important/);
      });
    });
  });
  </script>
</body>
</html>
